/*-------------------------------------------------------------------------
 - Copyright (c) 2024-2025 [XD-AMCC TEAM]
 - [XD-AMCC] is licensed under Mulan PSL v2.
 - You can use this software according to the terms and conditions of the Mulan PSL v2.
 - You may obtain a copy of Mulan PSL v2 at:
 -          http://license.coscl.org.cn/MulanPSL2
 - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
 - OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 - TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 - See the Mulan PSL v2 for more details.
 ------------------------------------------------------------------------*/


/**
 * @brief Implement API for C-strings.
 *
 * @author  WXQ#XDU
 * @date    2024.11
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "../include/amcc_string.h"


char * cstring_new(const char * cString, int maxLen) {
    if (maxLen < 0) maxLen = 0;
    int len  = maxLen;
    char * text = (char*)malloc(len + 1);
    if (NULL == text) {
        return NULL;
    }

    char * pDest = text;
    for (int i=0; i < maxLen; ++i) {  // copy up to maxLen chars
        if ('\0' == (*(pDest++) = *(cString++)) ) {
            len = i;  // the actual length < maxLen
            break;
        }
    }
    text[len] = '\0';
    return text;
}

void cstring_destroy(char * pThis) {
    free(pThis);
}


const char * amcc_long_spacestring() {
    static char * longSpace = // Maximum indentation is 200 columns
            "                                                  "  // 50 space
            "                                                  "
            "                                                  "
            "                                                  "  // 200 space
    ;

    return longSpace;
}

char * char2Cstring(char * buff, char c) {
    if ( c < 0x20  ||  c >= 0x7f ) {
        switch( c ){
            case '\t' :  strcpy(buff, "'\\t'"); break;  // horizontal Tab, HT
            case '\r' :  strcpy(buff, "'\\r'"); break;  // carriage Return, CR
            case '\n' :  strcpy(buff, "'\\n'"); break;  // Newline, NL,LF
            case '\f' :  strcpy(buff, "'\\f'"); break;  // Form feed, FF
            case '\v' :  strcpy(buff, "'\\v'"); break;  // Vertical tab, VT
            case '\a' :  strcpy(buff, "'\\a'"); break;  // Alert, BEL
            case '\b' :  strcpy(buff, "'\\b'"); break;  // Backspace, BS
            default   :
                snprintf(buff, 6+1, "'\\x%02X'", (unsigned char)c);
                break;
        }
    } else {
        int i = -1;
        buff[++i] = '\'';
        switch( c ) {
            case '\\' :    buff[++i] = '\\';  buff[++i] = '\\';   break;
            case '\'' :    buff[++i] = '\\';  buff[++i] = '\'';   break;
            default   :    buff[++i] = c;     break;
        }
        buff[++i] = '\'';
        buff[++i] = '\0';
    }

    return buff;
}


struct  t_escapedChar_pair {
    char  escaped_char;
    char  value;
};

// common C escaped-sequence:  \ ['"?abfnrtv\\]
static struct  t_escapedChar_pair cEscapedChars [] = {
        {'n', '\n'} ,
        {'r', '\r'} ,
        {'f', '\f'} ,
        {'t', '\t'} ,
        {'a', '\a'} ,
        {'b', '\b'} ,
        {'v', '\v'} ,
        {'\'', '\''} ,
        {'\"', '"'} ,
        {'\?', '?'}
};

int  charLiteral2Int(const char* literal) {
    const char * p = literal + 1;   // skip leading char: '
    if ('\'' == *p)  return 0;      // '' means a zero char
    if ('\\' != *p)  return *p;     // a single char = non-escaped

    //  scan an escaped sequence ...
    ++p;                // skip flag char: '\\'
    switch ( *p ) {
        case '\0' : return 0;
        case 'x' : case 'X':   case '0' :  case '1' :  case '2' :
        case '3' : case '4' :  case '5' :  case '6' :  case '7' :
            break;
        default:
            for(int i = 0; i<sizeof(cEscapedChars)/sizeof(cEscapedChars[0]); ++i) {
                if(*p == cEscapedChars[i].escaped_char) {
                    return cEscapedChars[i].value;
                }
            }
            return 0;
    }

    // convert a octal sequence, a hex sequence to a int
    int result = 0;
    if ('0' <= *p && *p <= '7') {  // octal , 1~3 chars
        for(int len=0 ; len<3 && '0' <= *p && *p <= '7' ; ++p, ++len) {
            result = (result << 3) + (*p - '0');
        }
        return (result < 0) ? -1 : result;
    }
    if (*p == 'x' || *p == 'X') { // hex , 1~2 chars
        ++p;
        for(int len=0; len<2 && '\0' != *p; ++p, ++len) {
            if     ('0' <= *p && *p <= '9') result = (result << 4) + (*p - '0');
            else if('a' <= *p && *p <= 'f') result = (result << 4) + (*p - 'a' + 10);
            else if('A' <= *p && *p <= 'F') result = (result << 4) + (*p - 'A' + 10);
            else break;
        }
        return (result < 0) ? -1 : result;
    }

    //assert ( *p == '\'');
    return 0;
}


char * cstring_duplicate(const char * source) {
#ifdef AMCC_BY_MSVC
    return _strdup(source) ;
#elif __STDC_VERSION__ >= 202311L || defined(AMCC_BY_GNU)
    return strdup(source) ;
#else
    if (NULL == source) return NULL;
    size_t len = strlen(source);
    char* copy = (char*)malloc(len + 1);
    strcpy(copy, source);
#endif
}


char * cstring_substr(const char * source, int from, int maxLen) {
    if (NULL == source || from < 0) return NULL;
    size_t srcLength = strlen(source);
    if (from >= srcLength)  return NULL;

    size_t N = srcLength - from;
    if (maxLen > 0 && maxLen < N ) N = maxLen;

    char* substr = (char*)malloc(N + 1);
    strncpy(substr, source + from, N);
    substr[N] = '\0';
    return substr;
}


int cstring_cmp_icase(const char* s1, const char* s2) {
#ifdef AMCC_BY_GNU
    return strcasecmp(s1, s2);
#else
    return _stricmp(s1, s2);
#endif
}


char * cstring_toXMLstring(char * buff, const char * src) {
    buff[0] = '\0';
    if ( NULL == src ) return buff;

    int    n;
    char * dest = buff;
    for ( const char* p = src;  *p != '\0'; ++p ) {
        switch (*p) {
            case '\'' :  strcpy(dest, "&apos;");  n = 6; break;
            case '\"' :  strcpy(dest, "&quot;");  n = 6; break;
            case '<' :   strcpy(dest, "&lt;");    n = 4; break;
            case '>' :   strcpy(dest, "&gt;");    n = 4; break;
            case '&' :   strcpy(dest, "&amp;");   n = 5; break;
            default :
                *dest = *p;  n = 1; break;
        }
        dest += n;
    }

    *dest = '\0';
    return buff;
}